package com.yunsoft.gateway.interceptor;

import com.yunsoft.gateway.annotation.WebInterceptor;
import com.yunsoft.gateway.interceptor.bean.WebInterceptorBean;
import com.yunsoft.gateway.interceptor.handler.AuthHandlerInterceptor;
import com.yunsoft.gateway.interceptor.handler.GatewayHandlerInterceptor;
import com.yunsoft.gateway.interceptor.handler.InternalHandlerInterceptor;
import com.yunsoft.gateway.interceptor.handler.ResubmitHandlerInterceptor;
import com.yunsoft.gateway.utils.SpringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.util.AntPathMatcher;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.HandlerMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.util.UrlPathHelper;

public class RegistrationInterceptor implements ApplicationListener<ContextRefreshedEvent>,HandlerInterceptor {
	
	private final static Logger LOGGER = LoggerFactory.getLogger(RegistrationInterceptor.class);

	/**
	 * 拦截器
	 */
	private List<WebInterceptorBean> interceptorAdapterList;
	/**
	 * 存储拦截器的线程变量
	 */
	private ThreadLocal<LinkedList<WebInterceptorBean>> ADAPTER_LIST = new ThreadLocal<LinkedList<WebInterceptorBean>>();
	/**获取url*/
	private UrlPathHelper urlPathHelper = new UrlPathHelper();
	/**匹配器*/
	private AntPathMatcher pathMatcher = new AntPathMatcher();
	
	String LOOKUP_PATH = HandlerMapping.class.getName() + ".lookupPath";
	/**
	 * 内置拦截器
	 */
	private final static List<String> WHITE_LIST = new ArrayList<String>();
	static {
		WHITE_LIST.add(GatewayHandlerInterceptor.class.getName());
		WHITE_LIST.add(InternalHandlerInterceptor.class.getName());
		WHITE_LIST.add(AuthHandlerInterceptor.class.getName());
		WHITE_LIST.add(ResubmitHandlerInterceptor.class.getName());
	}

	/**
	 * spring启动完后执行
	 * @param event
	 */
	@Override
	public void onApplicationEvent(ContextRefreshedEvent event) {
		if (event.getApplicationContext().getParent() == null) {
			init();
		}
	}

	/**
	 * 初始化
	 */
	private void init() {
		if(interceptorAdapterList != null) {
			return;
		}
		synchronized(this) {
			if(interceptorAdapterList != null) {
				return;
			}
			interceptorAdapterList = new ArrayList<WebInterceptorBean>();
			//获取所有定义的拦截器
			Map<String, InterceptorAdapter> adapterMap = SpringUtils.getBeanOfType(InterceptorAdapter.class);
			if(adapterMap == null || adapterMap.isEmpty()) {
				return;
			}
			//获取拦截器信息转成WebInterceptorBean
			Set<String> keys = adapterMap.keySet();
			for (String key : keys) {
				InterceptorAdapter adapter = adapterMap.get(key);
				WebInterceptor interceptor = adapter.getClass().getAnnotation(WebInterceptor.class);
				if(interceptor == null) {
					LOGGER.error("类{}上未发现com.fld.platform.springboot.annotation.WebInterceptor注解",adapter.getClass().getName());
					continue;
				}
				WebInterceptorBean bean = new WebInterceptorBean();
				bean.setAdapter(adapter);
				bean.setExcludePathPatterns(interceptor.excludePathPatterns());
				bean.setName(interceptor.name());
				bean.setOrder(interceptor.order());
				bean.setPathPatterns(interceptor.pathPatterns());
				interceptorAdapterList.add(bean);
			}
			if(CollectionUtils.isEmpty(interceptorAdapterList)) {
				return;
			}
			//
			check(interceptorAdapterList);
			//排序，先按order、在按name
			sort(interceptorAdapterList);
			//输出日志
			for (WebInterceptorBean bean : interceptorAdapterList) {
				LOGGER.info("拦截器：{}",bean.toString());
			}
		}
	}
	/**
	 * 检查，确保order必须大于5，确保GatewayHandlerInterceptor/InternalHandlerInterceptor/AuthHandlerInterceptor/ResubmitHandlerInterceptor/AdminHandlerInterceptor是按顺序执行，且不能存在重复的name
	 * @param adapterList
	 */
	private void check(List<WebInterceptorBean> adapterList) {
		//key=name
		Map<String,List<WebInterceptorBean>> duplicateNameMap = new HashMap<String,List<WebInterceptorBean>>();
		for (WebInterceptorBean bean : adapterList) {
			//order必须>=6，避免和内置拦截器冲突
			if(bean.getOrder() <= 5 && !WHITE_LIST.contains(bean.getAdapter().getClass().getName())) {
				throw new RuntimeException("类"+bean.getAdapter().getClass().getName()+"上的@WebInterceptor注解的order值必须>=6");
			}
			//名称
			List<WebInterceptorBean> duplicateNameList = duplicateNameMap.get(bean.getName());
			if(duplicateNameList == null) {
				duplicateNameList = new ArrayList<WebInterceptorBean>();
				duplicateNameMap.put(bean.getName(), duplicateNameList);
			}
			duplicateNameList.add(bean);
		}
		//name是否有重复
		if(!duplicateNameMap.isEmpty()) {
			Set<String> keys = duplicateNameMap.keySet();
			for (String key : keys) {
				List<WebInterceptorBean> duplicateNameList = duplicateNameMap.get(key);
				if(duplicateNameList.size() > 1) {
					throw new RuntimeException("拦截器名称："+key+"重复，重复数："+duplicateNameList.size()+"，重复类["+toStringArray(duplicateNameList)+"]");
				}
			}
		}
	}
	
	private String toStringArray(List<WebInterceptorBean> list) {
		StringBuilder builder = new StringBuilder("");
		for (WebInterceptorBean bean : list) {
			builder.append(bean.getAdapter().getClass().getName()+",");
		}
		builder.deleteCharAt(builder.length() - 1);
		return builder.toString();
	}
	
	/**
	 * 排序
	 * @param adapterList
	 */
	private void sort(List<WebInterceptorBean> adapterList) {
		if(CollectionUtils.isEmpty(adapterList)) {
			return;
		}
		//设置兼容算法
		System.setProperty("java.util.Arrays.useLegacyMergeSort", "true"); 	//使用兼容JDK1.7以下的排序算法
		//
		Collections.sort(adapterList, new Comparator<WebInterceptorBean>() {
			@Override
			public int compare(WebInterceptorBean bean1, WebInterceptorBean bean2) {
				int result = bean1.getOrder() - bean2.getOrder();
				if(result != 0) {
					return result;
				}
				//相等，按名称排序
				return bean1.getName().compareTo(bean2.getName());
			}
		});
	}
	/**
	 * 获取匹配的拦截器
	 * @return
	 */
	private LinkedList<WebInterceptorBean> getInterceptorHandlerAdapter(HttpServletRequest request) {
		if(CollectionUtils.isEmpty(interceptorAdapterList)) {
			return null;
		}
		//待匹配的url
		String lookupPath = urlPathHelper.getLookupPathForRequest(request, LOOKUP_PATH);
		if(LOGGER.isDebugEnabled()) {
			LOGGER.debug("lookupPath:{}",lookupPath);
		}
		//获取匹配
		LinkedList<WebInterceptorBean> adapterList = new LinkedList<WebInterceptorBean>();
		for (WebInterceptorBean adapter : interceptorAdapterList) {
			if (matches(adapter,lookupPath)) {	//匹配上
				//重新new一个是为了不影响之前的值
				WebInterceptorBean bean = new WebInterceptorBean();
				bean.setAdapter(adapter.getAdapter());
				bean.setExcludePathPatterns(bean.getExcludePathPatterns());
				bean.setName(adapter.getName());
				bean.setOrder(adapter.getOrder());
				bean.setPathPatterns(adapter.getPathPatterns());
				bean.setPreHandle(false);
				//加入list
				adapterList.add(bean);
			}
		}
		return adapterList;
	}
	/**
	 * pattern 和  lookupPath匹配
	 * @param bean
	 * @param lookupPath
	 * @return
	 */
	private boolean matches(WebInterceptorBean bean,String lookupPath) {
		if (!ObjectUtils.isEmpty(bean.getExcludePathPatterns())) {
			for (String pattern : bean.getExcludePathPatterns()) {	//不包含的
				if (pathMatcher.match(pattern, lookupPath)) {
					if(LOGGER.isDebugEnabled()) {
						LOGGER.debug("interceptor:{},lookupPath:{} match exclude pattern:{}",bean.getName(),lookupPath,pattern);
					}
					return false;
				}
			}
		}
		if (ObjectUtils.isEmpty(bean.getPathPatterns())) {	//默认匹配所有的
			return true;
		}
		for (String pattern : bean.getPathPatterns()) {
			if (pathMatcher.match(pattern, lookupPath)) {
				if(LOGGER.isDebugEnabled()) {
					LOGGER.debug("interceptor:{},lookupPath:{} match include pattern:{}",bean.getName(),lookupPath,pattern);
				}
				return true;
			}
		}
		return false;
	}
	
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		//初始化
		init();
		//获取匹配的拦截器
		LinkedList<WebInterceptorBean> adapterList = getInterceptorHandlerAdapter(request);
		//无拦截器处理
		if(CollectionUtils.isEmpty(adapterList)) {
			return false;
		}
		//放入线程变量
		ADAPTER_LIST.set(adapterList);
		//开始执行拦截器
		//执行preHandle，有异常或者返回false时，直接执行triggerAfterCompletion，只有preHandle全部返回true才会执行postHandle
		boolean result = applyPreHandle(adapterList, request, response,handler);
		if(!result) {	//返回false，后面的afterCompletion，不会再执行，因为在applyPreHandle已经执行过了
			ADAPTER_LIST.remove();
		}
		return result;
	}
	/**
	 * 执行preHandle方法
	 * @param adapterList
	 * @param request
	 * @param response
	 * @param handler
	 * @throws Exception
	 */
	private boolean applyPreHandle(LinkedList<WebInterceptorBean> adapterList,HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
		//一个一个过滤器开始执行，只要其中一个preHandle有异常或者返回false，后面的preHandle将不在执行 ，直接执行 afterCompletion
		for(int i = 0; i < adapterList.size(); i++) {
        	  WebInterceptorBean bean = adapterList.get(i);
              //调用拦截器中的preHandle方法
        	  boolean result = bean.getAdapter().preHandle(request, response, handler);
        	  bean.setPreHandle(result);
              if(!result) {
                  // 如果preHandler方法返回false,则触发afterCompletion方法的执行
                  triggerAfterCompletion(adapterList,request, response, handler, (Exception)null);
                  return false;
              }
        }
		return true;
	}
	
	@Override
	public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
			ModelAndView modelAndView) throws Exception {
		//倒序执行postHandle
		LinkedList<WebInterceptorBean> adapterList = ADAPTER_LIST.get();
		if(CollectionUtils.isEmpty(adapterList)) {
			return;
		}
		//开始倒序执行postHandle
		for(int i = adapterList.size() - 1; i >= 0; i--) {
			WebInterceptorBean bean = adapterList.get(i);
			//只有preHandle返回true的才执行postHandle
			if(bean.isPreHandle()) {
				bean.getAdapter().postHandle(request, response, handler, modelAndView);
			}
		}
	}
	
	/**
	 * 执行triggerAfterCompletion
	 * @param adapterList
	 * @param request
	 * @param response
	 * @param handler
	 * @param ex
	 * @throws Exception
	 */
	private void triggerAfterCompletion(LinkedList<WebInterceptorBean> adapterList,HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        for(int i = adapterList.size() - 1; i >= 0; i--) {
        	WebInterceptorBean bean = adapterList.get(i);
        	//只有preHandle返回true的才执行afterCompletion方法
        	if(!bean.isPreHandle()) {
        		continue;
        	}
            try {
                //调用拦截器中的afterCompletion方法
                bean.getAdapter().afterCompletion(request, response, handler, ex);
            } catch (Throwable e) {
                LOGGER.error("HandlerInterceptor.afterCompletion threw exception", e);
            }
        }
    }
	
	@Override
	public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
			throws Exception {
		//倒序执行afterCompletion
		LinkedList<WebInterceptorBean> adapterList = ADAPTER_LIST.get();
		//移除
		ADAPTER_LIST.remove();
		//
		if(CollectionUtils.isEmpty(adapterList)) {
			return;
		}
		//开始倒序执行afterCompletion
		triggerAfterCompletion(adapterList, request, response, handler, ex);
	} 
	
}
